﻿using System;
using System.Numerics;

namespace Afiniczny
{
    internal class CipherAffineAlgorithm
    {
        protected int _a, _b;
        protected int _blockSize;
        protected int alphabetLenght;

        private Encrypter _encrypter;
        private Decrypter _decrypter;

        public CipherAffineAlgorithm()
        {
            _blockSize = 10;
            alphabetLenght = Alphabet.getAlphabetLenght();
            RandomAB();
            _encrypter = new Encrypter(this);
            _decrypter = new Decrypter(this);
        }

        public CipherAffineAlgorithm(int keyA, int keyB)
        {
            _a = keyA;
            _b = keyB;
            _blockSize = 10;
            _encrypter = new Encrypter(this);
            _decrypter = new Decrypter(this);
            alphabetLenght = Alphabet.getAlphabetLenght();
        }

        public int GetKeyA()
        {
            return _a;
        }

        public int GetKeyB()
        {
            return _b;
        }

        public int GetBlockSize()
        {
            return _blockSize;
        }

        public void SetBlockSize(int a_blockSize)
        {
            _blockSize = a_blockSize;
        }

        public void RandomAB()
        {
            _a = Random();
            while (!CheckKeyConditions(_a))
            {
                _a = Random();
            }
            System.Threading.Thread.Sleep(100);
            _b = Random();
        }

        public bool CheckKeyConditions(int keyA)
        {
            if (GCD.calculateGCD(keyA, alphabetLenght) == 1)
                return true;
            return false;
        }

        public int Random()
        {
            var rand = new Random();
            return rand.Next(alphabetLenght);
        }

        public string EncryptText(string text)
        {
            return _encrypter.EncryptText(text);
        }

        public string DecryptText(string cryptedText)
        {
            return _decrypter.DecryptText(cryptedText);
        }

        public void FillBlockToBlockSize(ref string a_block)
        {
            int toAdd = _blockSize - a_block.Length;
            for (int i = 0; i < toAdd; i++)
            {
                a_block += " ";
            }
        }

        public BigInteger GetBlockFromText(string a_text)
        {
            BigInteger result = 0;

            if (a_text.Length < _blockSize)
                FillBlockToBlockSize(ref a_text);

            for (int i = 0; i < _blockSize; i++)
            {
                BigInteger a = BigInteger.Pow(Alphabet.getAlphabetLenght(), i);
                result += Alphabet.getIndexOfChar(a_text[i]) * a;
            }
            return result;
        }

        public string DecodeBlockToString(BigInteger a_decryptedBlock)
        {
            string result = "";
            int lastVal = 0;
            for (int i = 0; i < _blockSize; i++)
            {
                if (i > 0)
                    a_decryptedBlock -= lastVal * BigInteger.Pow(alphabetLenght, i - 1);
                int charIndex = (int)(a_decryptedBlock / BigInteger.Pow(alphabetLenght, i) % alphabetLenght);
                result += Alphabet.getCharFromIndex(charIndex);
            }
            return result;
        }
    }
}
